import { PipeTransform, Injectable, BadRequestException } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Express } from 'express';

/**
 * 文件上传管道
 * 零隐患MFIS规范 - 解决隐患：#11 输入验证，#14 信息泄露
 */
@Injectable()
export class FileUploadPipe implements PipeTransform {
  private readonly maxFileSize: number;
  private readonly allowedMimeTypes: string[];
  private readonly allowedExtensions: string[];

  constructor(private readonly configService: ConfigService) {
    this.maxFileSize = this.configService.get<number>('MAX_FILE_SIZE', 10 * 1024 * 1024);
    this.allowedMimeTypes = [
      'image/jpeg',
      'image/jpg',
      'image/png',
      'image/tiff',
      'image/bmp',
    ];
    this.allowedExtensions = ['.jpg', '.jpeg', '.png', '.tiff', '.tif', '.bmp'];
  }

  transform(value: Express.Multer.File): Express.Multer.File {
    if (!value) {
      throw new BadRequestException('请上传文件');
    }

    // 验证文件大小
    if (value.size > this.maxFileSize) {
      throw new BadRequestException(
        `文件大小超过限制，最大允许 ${this.formatFileSize(this.maxFileSize)}`
      );
    }

    if (value.size === 0) {
      throw new BadRequestException('文件大小不能为0');
    }

    // 验证文件名
    if (!value.originalname || value.originalname.trim().length === 0) {
      throw new BadRequestException('文件名不能为空');
    }

    // 验证文件名长度
    if (value.originalname.length > 255) {
      throw new BadRequestException('文件名过长，最大允许255个字符');
    }

    // 验证文件名安全字符
    const unsafePattern = /[<>:"/\\|?*\x00-\x1f]/;
    if (unsafePattern.test(value.originalname)) {
      throw new BadRequestException('文件名包含不安全字符');
    }

    // 验证MIME类型
    if (!this.allowedMimeTypes.includes(value.mimetype)) {
      throw new BadRequestException(`不支持的文件类型: ${value.mimetype}`);
    }

    // 验证文件扩展名
    const fileExtension = value.originalname.toLowerCase().substring(value.originalname.lastIndexOf('.'));
    if (!this.allowedExtensions.includes(fileExtension)) {
      throw new BadRequestException(`不支持的文件扩展名: ${fileExtension}`);
    }

    // 验证MIME类型与扩展名一致性
    const expectedMime = this.getMimeTypeFromExtension(fileExtension);
    if (!value.mimetype.includes(expectedMime)) {
      throw new BadRequestException('文件类型与扩展名不匹配');
    }

    return value;
  }

  private getMimeTypeFromExtension(extension: string): string {
    const mimeMap = {
      '.jpg': 'jpeg',
      '.jpeg': 'jpeg',
      '.png': 'png',
      '.tiff': 'tiff',
      '.tif': 'tiff',
      '.bmp': 'bmp',
    };
    return mimeMap[extension] || '';
  }

  private formatFileSize(bytes: number): string {
    const units = ['B', 'KB', 'MB', 'GB'];
    let size = bytes;
    let unitIndex = 0;

    while (size >= 1024 && unitIndex < units.length - 1) {
      size /= 1024;
      unitIndex++;
    }

    return `${size.toFixed(1)} ${units[unitIndex]}`;
  }
}